

#include "includes.h"


extern void yield();

// Global variables 
unsigned int timeout = 0;
unsigned int num_tasks = 0;
struct task_info *task_ptr = 0;
struct task_info *last_task = 0;
struct task_info task_table[MAX_TASKS];

struct task_info *running_task = task_table;

int
create_task(void *function, void *argument, void *extra)
{
  struct task_info *c;

  if (num_tasks + 1 >= MAX_TASKS)
    {
      return -1;
    }
  
  int i = 1;
  while(task_table[i].state != ST_NOTASK)
    {
      i++;
      if (i == MAX_TASKS)
	{
	  return -1;
	}
    }

  num_tasks++;
  printf("Creating task in slot %d\n", i);
  c = &task_table[i];
  c->tid = i;

  c->ex_stack_limit = (void *) malloc(STACK_SIZE);
  c->ex_stack = c->ex_stack_start = ((void *) c->ex_stack_limit) + STACK_SIZE - sizeof(void *);

  c->op_stack = c->op_stack_start = (void *) malloc(STACK_SIZE);
  c->op_stack_limit = ((void *) c->op_stack_start) + STACK_SIZE;

  c->heap = c->heap_start = (void *) malloc(HEAP_SIZE);
  c->heap_limit = ((void *) c->heap_start) + HEAP_SIZE;
  
  c->numargs = 1;

  // Set up execution stack
  *c->ex_stack = argument;       // argument to compiled function
  c->ex_stack--;
  *c->ex_stack = task_cleanup;   // last return address
  c->ex_stack--;
  *c->ex_stack = function;       // function address to "return" to


  //The following is only valid on i386.
  c->ex_stack--;
  *c->ex_stack = (void *) 0xaaaaaaaa;  // "Saved" registers
  c->ex_stack--;
  *c->ex_stack = (void *) 0xbbbbbbbb;
  c->ex_stack--;
  *c->ex_stack = (void *) 0xcccccccc;
  c->ex_stack--;
  *c->ex_stack = (void *) 0xdddddddd;


  // Set up op stack.
  *c->op_stack = (void *) c->tid;
  c->op_stack++;
  *c->op_stack = extra;
  c->op_stack++;  // set up frame pointer
  c->frame = c->op_stack;
  *c->op_stack = 0x0;

  c->state = ST_RUNNING;

  return c->tid;
}


void
task_cleanup()
{
  num_tasks--;
  task_ptr->state = ST_HALTED;
  printf(">>> Task %d complete\n", task_ptr->tid);
  yield();
}

static inline void*
copy_heap_obj(void *arg, tid_t to_id)
{
  yjk_obj_header *fo = (void *) ((int) arg & 0xfffffffc);
  yjk_obj_header *to = NULL;
  
  switch (fo->type)
    {

    default:
      { 
	to = heap_alloc(fo->size - sizeof(yjk_obj_header), fo->type, &task_table[to_id]);
	memcpy(to, fo, fo->size);
	break;
      }
    }

  return (void *) ((int) to | 0x1);
}


void *
copy_obj(void *arg, tid_t to_id)
{
  switch ((int) arg & 0x3)
    {
    case TAG_INT:
      return arg;
      
    case TAG_PTR:
      return copy_heap_obj(arg, to_id);

    default:
      break;
    }

  return NULL;
}


void
grow_op_stack()
{
  //FIXME: have to relocate frame pointers
  if (NULL == realloc(task_ptr->op_stack_start, 
		      ((unsigned int) task_ptr->op_stack_limit - 
		       (unsigned int) task_ptr->op_stack_start) * 2))
    {
      printf("Out of op stack memory for task %d.\n", task_ptr->tid);
      exit(1);
    }
}


void
print_stack()
{
  void **j = task_ptr->op_stack;
  void **k = task_ptr->frame;
  printf("--- OP STACK DUMP TID[%d] ---\n", task_ptr->tid);
  
  int l = 0;
  while (j >= task_ptr->op_stack_start)
    {
      if (j == k)
	{
	  printf(" 0x%08x:  0x%08x - FRAME %d\n", (int) j, (int) *j, l);
	  k = *k;
	  l++;
	}
      else
	{
	  printf(" 0x%08x:  0x%08x | \n", (int) j, (int) *j);
	}
      j--;
    }
  
  printf("--- END STACK DUMP ---\n");
}


void
print_ex_stack()
{
  void **j = task_ptr->ex_stack;
  printf("--- EXEC STACK DUMP TID[%d] ---\n", task_ptr->tid);
  while (j < task_ptr->ex_stack_start)
    {
      printf("0x%x: 0%x\n", (int) j, (int) *j);
      j++;
    }
  printf("--- END STACK DUMP ---\n");
}


void
check_stack_bounds()
{
  //FIXME: Should check stack bounds better here
  if ((int) task_ptr->ex_stack < ((int) task_ptr->ex_stack_limit) + 0x500)
    {
      printf("Stack overflow: 0x%x (start: 0x%x -> limit: 0x%x)\n", 
	     (int) task_ptr->ex_stack, 
	     (int) task_ptr->ex_stack_start, 
	     (int) task_ptr->ex_stack_limit);
      exit(1);
    }
  
  if ((int) task_ptr->op_stack > ((int) task_ptr->op_stack_limit) - 0x100)
    {
      printf("Op Stack overflow: 0x%x (start: 0x%x -> limit: 0x%x)\n", 
	     (int) task_ptr->op_stack, 
	     (int) task_ptr->op_stack_start, 
	     (int) task_ptr->op_stack_limit);
      exit(1);
    }
}


void
schedule()
{
  //task_ptr = running_task;
  do 
    {
      // Send message
      if (task_ptr->state == ST_SEND)
	{
	  task_ptr->state = ST_RUNNING;
	  printf("Sending %x to %d\n", 
		 (int) task_ptr->send_arg, 
		 (int) task_ptr->send_to);
	  
	  if ((unsigned int) task_ptr->send_to < MAX_TASKS) // local task
	    {
	      struct message *m = task_table[(int) task_ptr->send_to].msg;
	      if (m == NULL)
		{
		  m = task_table[(int) task_ptr->send_to].msg = malloc(sizeof(struct message));
		}
	      else 
		{
		  while (m->next != NULL)
		    {
		      m = m->next;
		    }
		  m->next = malloc(sizeof(struct message));
		  m = m->next;
		}

	      m->from = task_ptr->tid;
	      m->arg = copy_obj(task_ptr->send_arg, (int) task_ptr->send_to);
	      m->next = NULL;
	      printf("Message sent from %d %x\n", (int) m->from, (int) m->arg);
	    }

	  task_table[(int) task_ptr->send_to].state = ST_RECV;
	}

      else if (task_ptr->state == ST_RECV)
	{
	  printf("Receiving not implemented yet. Task (%d)\n", task_ptr->tid);
	  task_ptr->state = ST_RUNNING;
	}

      task_ptr++;

      if (task_ptr->state == ST_HALTED)
	{
	  free(task_ptr->op_stack_start);
	  free(task_ptr->ex_stack_limit);
	  task_ptr->state = ST_NOTASK;
	  task_ptr->op_stack = task_ptr->ex_stack = 0x0;
	  task_ptr->tid = TID_NONE;
	}


      int offset = task_ptr - task_table;
      if (offset >= MAX_TASKS)
	{
	  task_ptr = &task_table[1];
	}

      //return to system control
      if (num_tasks == 0) 
	{
	  task_ptr = &task_table[0];
	  task_ptr->msg = 0;
	  break;
	}
    } while (task_ptr->state != ST_RUNNING);
  //running_task = task_ptr;
}

